package com.csap.framework.oauth2.server;

import cn.dev33.satoken.LoginUtil;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.oauth2.PasswordLogin;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Handle;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
import com.csap.framework.annotation.ResponseAdvice;
import com.csap.framework.annotation.apidoc.Api;
import com.csap.framework.annotation.apidoc.ApiOperation;
import com.csap.framework.annotation.apidoc.ApiProperty;
import com.csap.framework.annotation.apidoc.ApiPropertys;
import com.csap.framework.extension.result.ResultParam;
import com.csap.framework.oauth2.UserInfo;
import com.csap.framework.oauth2.UserInfoEndpoint;
import com.csap.framework.oauth2.request.AuthorizeRequest;
import com.csap.framework.oauth2.request.DoConfirmRequest;
import com.csap.framework.oauth2.request.LogoutRequest;
import com.csap.framework.oauth2.request.MobileLoginRequest;
import com.csap.framework.oauth2.request.PlatformRequest;
import com.csap.framework.oauth2.request.RegisterRequest;
import com.csap.framework.oauth2.request.TokenRequest;
import com.csap.framework.oauth2.request.UpdateRequest;
import com.csap.framework.oauth2.response.RegisterResponse;
import com.csap.framework.oauth2.server.model.PlatformModel;
import com.csap.framework.oauth2.server.service.Oauth2ServerService;
import com.csap.framework.oauth2.server.service.PlatformService;
import com.csap.framework.oauth2.server.service.UserOpenInfoService;
import com.csap.framework.properties.apidoc.ParamType;
import com.csap.framework.util.Assert;
import com.csap.framework.util.StringUtil;
import com.csap.framework.util.bean.BeanCopierUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.constraints.NotEmpty;
import java.util.Objects;

/**
 * Server端
 *
 * @author yangchengfu
 * @date 2021/7/25 11:37 下午
 **/
@RestController
@ResponseAdvice
@Api("授权服务API")
@Slf4j
public class Oauth2ServerEndpoint implements Oauth2ServerEndpointApi, UserInfoEndpoint {

    @Resource
    private Oauth2ServerService oauth2ServerService;
    @Resource
    private PlatformService platformService;
    @Resource
    private UserOpenInfoService userOpenInfoService;

    @Override
    public Boolean logoutByOpenId(LogoutRequest logoutRequest) {
        LoginUtil.getInstance().logoutByLoginId(userOpenInfoService.getUserId(logoutRequest.getClient_id(), logoutRequest.getOpenId()));
        return Boolean.TRUE;
    }

    /**
     * 模式一：Code授权码/隐藏式登录
     *
     * @param authorizeRequest 请求数据
     * @return 返回结果
     */
    @Override
    @ApiOperation(value = "模式一：Code授权码/隐藏式登录", description = "根据response_type决定", paramType = ParamType.QUERY)
    @GetMapping("/oauth2/authorize")
    public Object authorize(AuthorizeRequest authorizeRequest) {
        return SaOAuth2Handle.authorize(SaHolder.getRequest(), SaHolder.getResponse(), SaOAuth2Manager.getConfig());
    }

    /**
     * 模式一，模式三，模式四：Code授权码 获取 Access-Token
     *
     * @param tokenRequest 授权数据
     * @return 返回token
     */
    @Override
    @ApiOperation(value = "模式一，模式三，模式四：Code授权码 获取 Access-Token", description = "" + "模式一：Code授权码 获取 Access-Token" + "Refresh-Token 刷新 Access-Token" + "模式三：密码式" + "模式四：凭证式", paramType = ParamType.QUERY)
    @RequestMapping("/oauth2/token")
    public Object token(TokenRequest tokenRequest) {
        if (Objects.isNull(tokenRequest.getGrant_type())) {
            log.info("token :{}", tokenRequest);
        }
        Assert.notNull(tokenRequest.getGrant_type(), "授权类型不能为空");
        return tokenRequest.getGrant_type().getCmd().apply(tokenRequest);
    }

    /**
     * 模式一：授权登录
     *
     * @param username 账号
     * @param password 密码
     * @return 返回登录结果
     */
    @Override
    @ApiOperation(value = "模式一：doLogin 登录接口", paramType = ParamType.QUERY)
    @ApiPropertys({@ApiProperty(value = "登录账号", name = "username", required = true), @ApiProperty(value = "登录密码", name = "password", required = true)})
    @GetMapping("/oauth2/doLogin")
    public Object doLogin(@RequestParam @NotEmpty(message = "登录账号不能为空") String username, @RequestParam @NotEmpty(message = "登录密码不能为空") String password) {
        return SaOAuth2Manager.getConfig().doLoginHandle.apply(username, password);
    }

    /**
     * 模式一：确认授权
     *
     * @param confirmRequest 参数
     * @return 返回授权结果
     */
    @Override
    @ApiOperation(value = "模式一：doConfirm 确认授权接口", paramType = ParamType.QUERY)
    @RequestMapping("/oauth2/doConfirm")
    public Object doConfirm(DoConfirmRequest confirmRequest) {
        return SaOAuth2Handle.doConfirm(SaHolder.getRequest());
    }

    /**
     * 注册
     *
     * @param registerRequest 请求参数
     * @return 结果
     */
    @Override
    @ApiOperation(value = "注册", paramType = ParamType.BODY)
    @PostMapping("/oauth2/register")
    public RegisterResponse register(@RequestBody RegisterRequest registerRequest) {
        registerRequest.setPlatform(platform(registerRequest.getPlatform()));
        oauth2ServerService.isRegister(registerRequest, Boolean.TRUE);
        return oauth2ServerService.register(registerRequest);
    }

    @Override
    @ApiOperation(value = "根据opendId删除用户", paramType = ParamType.QUERY)
    @ApiPropertys({@ApiProperty(value = "用户的openId", name = "openId", required = true), @ApiProperty(value = "客户端ID", name = "clientId", required = true)})
    @DeleteMapping("/oauth2/removeByOpenId")
    public Boolean removeByOpenId(@RequestParam @NotEmpty(message = "openId不能为空") String openId, @RequestParam @NotEmpty(message = "clientId不能为空") String clientId) {
        return oauth2ServerService.removeByOpenId(openId, clientId);
    }

    /**
     * 一键登录只支持-账号密码
     *
     * @param registerRequest 请求参数
     * @return 返回结果
     */
    @Override
    @ApiOperation(value = "一键登录", paramType = ParamType.BODY)
    @PostMapping("/oauth2/loginRegister")
    public ResultParam<AccessTokenModel> loginRegister(@RequestBody RegisterRequest registerRequest) {
        registerRequest.setPlatform(platform(registerRequest.getPlatform()));
        oauth2ServerService.loginRegister(registerRequest);
        return SaOAuth2Handle.password(PasswordLogin.builder().userName(registerRequest.getUserName()).password(registerRequest.getPassword()).clientId(registerRequest.getClientId()).platform(registerRequest.getPlatform()).clientSecret(registerRequest.getClientSecret()).scope("").loginType(registerRequest.getLoginType()).build());
    }

    /**
     * 一键注册，只注册不登录，不判断账号是否存在
     *
     * @param registerRequest 请求参数
     * @return 返回结果
     */
    @Override
    @ApiOperation(value = "一键注册", paramType = ParamType.BODY)
    @PostMapping("/oauth2/onekeyRegister")
    public RegisterResponse onekeyRegister(@RequestBody RegisterRequest registerRequest) {
        registerRequest.setPlatform(platform(registerRequest.getPlatform()));
        SaOAuth2Util.checkClientSecret(registerRequest.getClientId(), registerRequest.getClientSecret());
        oauth2ServerService.loginRegister(registerRequest);
        return BeanCopierUtil.copy(registerRequest, RegisterResponse.class);
    }

    /**
     * 一键登录只支持-手机号
     *
     * @param registerRequest 请求参数
     * @return 返回结果
     */
    @Override
    @ApiOperation(value = "一键登录", paramType = ParamType.BODY)
    @PostMapping("/oauth2/loginRegisterMobile")
    public AccessTokenModel loginRegisterMobile(@RequestBody RegisterRequest registerRequest) {
        registerRequest.setPlatform(platform(registerRequest.getPlatform()));
        oauth2ServerService.loginRegister(registerRequest);
        return oauth2ServerService.loginMobile(registerRequest.getMobile(), registerRequest.getPlatform(), registerRequest.getClientId(), registerRequest.getDeviceType(), "");
    }

    /**
     * 根据手机号码登录
     *
     * @param mobileRequest 手机号登陆参数
     * @return 返回登录的token
     */
    @Override
    @ApiOperation(value = "根据手机号码登录", paramType = ParamType.BODY)
    @PostMapping("/oauth2/loginMobile")
    public AccessTokenModel loginMobile(@RequestBody MobileLoginRequest mobileRequest) {
        mobileRequest.setPlatform(platform(mobileRequest.getPlatform()));
        return oauth2ServerService.loginMobile(mobileRequest.getMobile(), mobileRequest.getPlatform(), mobileRequest.getClient_id(), mobileRequest.getDeviceType(), "");
    }

    /**
     * 获取Userinfo信息：昵称、头像、性别等等
     *
     * @return 用户信息
     */
    @Override
    @ApiOperation(value = "获取Userinfo信息", paramType = ParamType.QUERY)
    @ApiPropertys({@ApiProperty(value = "登录类型", name = "loginType", required = true, paramType = ParamType.HEADER)})
    public UserInfo userInfo() {
        return userInfo(LoginUtil.getInstance().getLoginId().toString());
    }

    /**
     * 根据OpendId修改用户信息
     *
     * @param updateRequest 修改的参数
     * @return 是否修改成功
     */
    @Override
    @PutMapping("/oauth2/updateByOpenId")
    @ApiOperation(value = "根据OpenId修改用户信息", paramType = ParamType.BODY)
    public Boolean updateByOpenId(@RequestBody UpdateRequest updateRequest) {
        updateRequest.setPlatform(platform(updateRequest.getPlatform()));
        return oauth2ServerService.updateByOpenId(updateRequest);
    }

    /**
     * 退出登录
     */
    @Override
    @ApiOperation(value = "退出登录")
    public void loginOut() {
        LoginUtil.getInstance().logout();
    }

    @Override
    public UserInfo userInfo(Object loginId) {
        return oauth2ServerService.userInfo(loginId);
    }

    @Override
    public Boolean addPlatform(PlatformRequest platformRequest) {
        return platformService.insert(BeanCopierUtil.copy(platformRequest, PlatformModel.class));
    }

    @Override
    public Boolean updatePlatform(PlatformRequest platformRequest) {
        return platformService.update(BeanCopierUtil.copy(platformRequest, PlatformModel.class));
    }

    /**
     * 平台处理
     *
     * @param platform 平台
     * @return 最终平台
     */
    private String platform(String platform) {
        if (StringUtil.isEmpty(platform)) {
            platform = LoginUtil.platform();
        }
        Assert.notEmpty(platform, "平台标识不能为空");
        platformService.findByCode(platform);
        return platform;
    }
}
